﻿#region COPYRIGHT© 2005-2013 Phillip Clark. All rights reserved.

// For licensing information see License.txt (MIT style licensing).

#endregion

using System;
using System.Diagnostics.Contracts;

namespace FlitBit.Parse
{
	/// <summary>
	/// Base match rule for token specs; defines validity rules for tokens.
	/// </summary>
	public sealed partial class TokenSpec : DelegatedMatch
	{
		/// <summary>
		/// Specification of the special EOF token.
		/// </summary>
		public static readonly TokenSpec EofSpec = new TokenSpec("EOF", TokenMetaKind.Eof, NullMatch);
		/// <summary>
		/// Specification of the special error token.
		/// </summary>
		public static readonly TokenSpec ErrorSpec = new TokenSpec("ERROR", TokenMetaKind.Error, NullMatch);

		readonly TokenFollowedBy _followedBy;
		readonly TokenMetaKind _kind;
		readonly string _name;
		readonly TokenScopeInfluence _scopeInfluence;

		internal TokenSpec(string name, TokenMetaKind kind, Match rule)
			: this(name, kind, rule, TokenFollowedBy.Any, TokenScopeInfluence.None)
		{}

		internal TokenSpec(string name, TokenMetaKind kind, Match rule, TokenFollowedBy followdBy)
			: this(name, kind, rule, followdBy, TokenScopeInfluence.None)
		{}

		internal TokenSpec(string name, TokenMetaKind kind, Match rule, TokenFollowedBy followdBy,
			TokenScopeInfluence scopeInfluence)
			: base(rule)
		{
			Contract.Requires(name != null);

			_name = name;
			_kind = kind;
			_followedBy = followdBy;
			_scopeInfluence = scopeInfluence;
		}

		/// <summary>
		/// Gets the follow-by spec for the rule.
		/// </summary>
		public TokenFollowedBy FollowedBy { get { return _followedBy; } }
		/// <summary>
		/// Gets the token's metakind.
		/// </summary>
		public TokenMetaKind MetaKind { get { return _kind; } }
		/// <summary>
		/// Gets the token's name.
		/// </summary>
		public string Name { get { return _name; } }
		/// <summary>
		/// Gets the token's scope influence.
		/// </summary>
		public TokenScopeInfluence ScopeInfluence { get { return _scopeInfluence; } }

		/// <summary>
		/// Fluent specification; adds a follow-by spec to the rule, returning a new instance.
		/// </summary>
		/// <param name="tokenFollowedBy"></param>
		/// <returns></returns>
		public TokenSpec IsFollowedBy(TokenFollowedBy tokenFollowedBy)
		{
			return new TokenSpec(_name, _kind, this.InnerMatch, tokenFollowedBy);
		}

		/// <summary>
		/// Tries to match the rule.
		/// </summary>
		/// <typeparam name="TKind">token kind TKind</typeparam>
		/// <param name="input">the input</param>
		/// <param name="cursor">a cursor position within the input</param>
		/// <param name="kind">the kind being matched</param>
		/// <param name="token">an output token that is set upon success; <em>null</em> otherwise.</param>
		/// <returns><em>true</em> if matched; otherwise <em>false</em></returns>
		public bool TryMatch<TKind>(string input, ref Cursor cursor, TKind kind, out Token<TKind> token)
			where TKind : struct
		{
			Capture capture;
			if (TryCapture(input, ref cursor, out capture))
			{
				token = new Token<TKind>(this, kind, capture);
				return true;
			}
			token = default(Token<TKind>);
			return false;
		}

		/// <summary>
		/// Tries to match the rule.
		/// </summary>
		/// <typeparam name="TKind">token kind TKind</typeparam>
		/// <param name="input">the input</param>
		/// <param name="cursor">a cursor position within the input</param>
		/// <param name="kind">the kind being matched</param>
		/// <param name="token">an output token that is set upon success; <em>null</em> otherwise.</param>
		/// <returns><em>true</em> if matched; otherwise <em>false</em></returns>
		public bool TryMatch<TKind>(char[] input, ref Cursor cursor, TKind kind, out Token<TKind> token)
			where TKind : struct
		{
			Capture capture;
			if (TryCapture(input, ref cursor, out capture))
			{
				token = new Token<TKind>(this, kind, capture);
				return true;
			}
			token = default(Token<TKind>);
			return false;
		}


		/// <summary>
		/// Fluent specification; adds a scope influence to the rule, returning a new instance.
		/// </summary>
		/// <param name="influence"></param>
		/// <returns></returns>
		public TokenSpec WithScopeInfluence(TokenScopeInfluence influence)
		{
			return new TokenSpec(_name, _kind, this.InnerMatch, _followedBy, influence);
		}

		/// <summary>
		/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
		/// </returns>
		/// <filterpriority>2</filterpriority>
		public override string ToString()
		{
			return String.Concat("{ MetaKind: ", this._kind
													, ", Name: ", this._name
													, ", Match: ", this.InnerMatch
													, ", FollowedBy: ", this._followedBy
													, "}");
		}
	}
}